/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.ubinow.mflex.baseui;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

import net.ubinow.mflex.events.Event;
import net.ubinow.mflex.util.Constants;
import net.ubinow.mflex.util.MiscUtil;


/**
 *
 * @author Josh
 */
public class ExpandableWidgetContainer extends WidgetContainer {
   
    private boolean _expanded = false; 
    private int _currentHeight = 0;
    private int _baseHeight = -1;
    private boolean _expandOnFocus = false; // either expand on focus or expand on click - default to expand on click - if set to true then expand on focus or click and collapsed on off focus
    private long _expandTimeStamp = 0; // timestamp when this component was expanded or collapsed (for for delaying expanding and collapsing)    
    private int _expandDelay = 0; // delay to expand the expandable widget container 
    private int _collapseDelay = 0; // delay to collapse the expandable widget container 
    
    private AbsWidget _baseWidget = null;
    
    private WidgetContainer _parentContainer = null;
    
    public ExpandableWidgetContainer( WidgetContainer parentContainer, AbsWidget baseWidget, boolean showExpanded, int width ) {
        super();
        
        _parentContainer = parentContainer;
        
        setupBaseWidget( baseWidget, showExpanded );
        
        _expandDelay = Constants.AD_DEFAULT_EXPANDABLE_CONTAINER_EXPAND_DELAY;
        _collapseDelay = Constants.AD_DEFAULT_EXPANDABLE_CONTAINER_COLLAPSE_DELAY; 
        
        _width = width;
        
        _paddingY = 0;
        _paddingX = 0;
        _widgetPaddingH = Constants.AD_WIDGET_PADDING;
        setLoopingWidgetNav( false );
    }
    
    public void setupBaseWidget( AbsWidget baseWidget, boolean showExpanded ){
        if( baseWidget != null ){        
            _baseWidget = baseWidget;
            _baseHeight = _baseWidget.getTotalHeight() + _widgetPaddingH;
            _currentHeight = _height = _baseHeight + _widgetPaddingH;
            
            // register ON_FOCUS and SELECT_DOWN so we can expand/collapse this widget
            //_baseWidget.addEventListener( Event.EVT_ON_FOCUS, this );
            //_baseWidget.addEventListener( Event.EVT_OFF_FOCUS, this );
            _baseWidget.addEventSubscriber( Event.EVT_SELECT_DOWN, this );
        }
                        
        if( showExpanded || baseWidget == null ){
            setExpanded(true); 
        }        
    }
    
    /**
     * returns the expand delay (the length in time in whcih this will expand when on focus or clicked)
     * @return
     */
    public int getExpandDelay(){
        return _expandDelay;
    }
    
    /**
     * sets the expand delay; (the length in time in whcih this will expand when on focus or clicked)
     * @param delay
     */
    public void setExpandDelay( int delay ){
        _expandDelay = delay;
    }
    
    /**
     * returns the collapse delay (the length in time in whcih this will collapse when focus is taken off or clicked)
     * @return
     */
    public int getCollapseDelay(){
        return _collapseDelay;
    }
    
    /**
     * sets the collapse delay (the length in time in whcih this will collapse when focus is taken off or clicked)
     * @param delay
     */
    public void setCollapseDelay( int delay ){
        _collapseDelay = delay;
    }
    
    
    public void expand(){
        setExpanded(true); 
    }
    
    public void collapse(){
        setExpanded(false); 
    }        

    public void setExpanded(boolean expanded) {
        _expandTimeStamp = System.currentTimeMillis();
        this._expanded = expanded;
    }
    
    public boolean isExpanded(){
        return _expanded;
    }
    
    /**
     * helper method that returns TRUE if the contents should be visible (when the state of this widget is flagged for expansion (_expanded == true) 
     * and the expanded delay has elapsed)
     * @return
     */
    private boolean showingContents(){
        return isExpanded() && ((System.currentTimeMillis()- _expandTimeStamp ) >= _expandDelay ) || !isExpanded();
    }
    
    /**
     * returns true if the widget container is fully expanded 
     * @return
     */
    public boolean isFullyExpanded(){
        return _height == _currentHeight;
    }
    
    public void setExpandOnFocus( boolean expandOnFocus ){
        _expandOnFocus = expandOnFocus;
    }
    
    public boolean isExpandOnFocus(){
        return _expandOnFocus;
    }
    
    public boolean handleKeyPressed( int action, int keyCode ) {
        if( _baseWidget == null ){
            return super.handleKeyPressed( action, keyCode );
        }
        else{
            if( action == Canvas.FIRE && _selectedWidget == -1 ){
                setExpanded(!isExpanded()); 
                return true; 
            }       
            else if( !isExpanded() ){
                return false; 
            }
            else{            
                if( !super.handleKeyPressed( action, keyCode ) ){
                    if( action == Canvas.UP && _selectedWidget == 0 ){
                        _widgets[_selectedWidget].setFocus( false , Constants.DIRECTION_UP );
                        _selectedWidget = -1;
                        return true; 
                    }
                    else{
                        return false; 
                    }
                }
                else{
                    return true; 
                }
            }
        }
            
    }
    
    public void cycle() {        
        super.cycle();
        
        // expanding and collapsing animation 
        if( isExpanded() && _currentHeight != _height && ((System.currentTimeMillis()- _expandTimeStamp ) >= _expandDelay ) ){
            _currentHeight += MiscUtil.getInstance().calcHalfDistance( _height, _currentHeight );
            
//            Integer selectIndex = null;
            
            if( _currentHeight > _height )
                _currentHeight = _height;
                
            if( _currentHeight == _height )
            	notifySubscribers( getEvet( Event.EVT_GUI_RESIZE, Boolean.TRUE ) );
            else
            	notifySubscribers( getEvet( Event.EVT_GUI_RESIZE, Boolean.FALSE ) );           
        }
        else if( _baseHeight != -1 && !isExpanded() && _currentHeight != _baseHeight && ((System.currentTimeMillis()-_expandTimeStamp) >=  _collapseDelay)){
            _currentHeight += MiscUtil.getInstance().calcHalfDistance( _baseHeight, _currentHeight );
            
            // flag the event so the parent knows what to do ie if the user has the last widget focused then 
            // we want to ensure that it is visble by calling adjustOffsetY on the parent to reposition the 
            // widgets on the parent container                                     
            
            if( _currentHeight < _baseHeight ){
                _currentHeight = _baseHeight;                
            }
            
            notifySubscribers( getEvet( Event.EVT_GUI_RESIZE, Boolean.FALSE ) );
        }
    }
    
    public void setFocus( boolean focus ,int direction ){               
        
        // if focus then make sure we have a selected widget and set focus on it 
        if( focus && _expandOnFocus ){
            setExpanded(true); 
        }
        else if( !focus ) {
//            System.out.println( "ExpandableWidgetContainer.setFocus " + focus );
            setExpanded(false);
        }
        
        // logic; if the widget container is open and the user sets its focus from the bottom then 
        // set focus on the last item of the widget 
        if( focus && (_baseWidget == null || isExpanded() || _expandOnFocus) && (direction == Constants.DIRECTION_UP || _baseWidget == null) ) {
            if( _widgetCount > 0 ){
                if( _selectedWidget == -1 ){
                    if( direction == Constants.DIRECTION_DOWN ){
                        for( int i=0; i<_widgetCount; i++ ){
                            if( _widgets[i].isSelectable() ){
                                _selectedWidget = i;
                                break;
                            }
                        }
                    }
                    else{
                        for( int i=_widgetCount-1; i>=0; i-- ){
                            if( _widgets[i].isSelectable() ){
                                _selectedWidget = i;
                                break;
                            }
                        }
                    }
                    
                    _widgets[_selectedWidget].setFocus( true ,-1 );
                    if( _baseWidget != null )
                        _baseWidget.setFocus( true, direction);
                }
            }
        }
        
        else if( focus ){
            if( _baseWidget != null )
                _baseWidget.setFocus( true, direction);
        }
        
        // if off focus then take away the focus of the selected widget
        else if( !focus ){
            if( _widgetCount > 0 ){
                if( _selectedWidget != -1 ){                                        
                    _widgets[_selectedWidget].setFocus( false ,-1 );
                    _selectedWidget = -1;
                }
                
                if( _baseWidget != null )
                    _baseWidget.setFocus( false, direction );
            }
        }
        
//        super.setFocus( focus ,-1 );
    }
    
    public void setPos( int x, int y ){
        super.setPos( x, y );        
    }
    
    public int getBottomY( boolean includeHeight ){
        if( !isExpanded() && _baseWidget != null ){
            return _y; 
        }
        if( _widgets == null || _widgetCount == 0 )
            return _y; 
        else if( _selectedWidget != -1 ){
            if( includeHeight )
                return _widgets[_selectedWidget].getY()+_widgets[_selectedWidget].getHeight()+_y;
            else
                return _widgets[_selectedWidget].getY()+_y;
        }
        else{
            if( includeHeight )
                return _widgets[_widgetCount-1].getY()+_y;
            else
                return _widgets[_widgetCount-1].getY()+_y;
        }
    }
    
    public int getBottomY(){
        return getBottomY( false );
    }
 
    public void setOffsetY( int offsetY ){
        // do nothing as we don't want this to move (otherwise it'll move over the other widgets held within the same container)
    }
    
     public void paint( Graphics g, int offsetX, int offsetY, int layer ) {
         if( !isExpanded() && _currentHeight == _baseHeight  && _baseWidget != null ){
             _baseWidget.paint( g, _x + offsetX, _y+_offsetY + offsetY, 0 );
         }
         else{
             // draw base #             
             if( _baseWidget != null ){
                _baseWidget.paint( g, _x + offsetX, _y + offsetY, 0 );
             }
             
             // draw a clipped version of the rest of the widget
//             MiscUtil.getInstance().clip( g, _x + offsetX, _y + offsetY + _offsetY, _width, _currentHeight ); 
             prevX = g.getClipX();
             prevY = g.getClipY();
             prevWidth = g.getClipWidth();
             prevHeight = g.getClipHeight();
             g.setClip( _x + offsetX, _y + offsetY, _width, _currentHeight );
             
             if( showingContents() )
                super.paint(g, offsetX, offsetY, layer, true );
             
//             MiscUtil.getInstance().releaseClip( g ); 
             g.setClip( prevX, prevY, prevWidth, prevHeight );
         }        
     }
     
     public void setWidgets( AbsWidget[] widgets ){
        _widgets = widgets;
        _widgetCount = _widgets.length;
        
        reinitSizeAndWidgetPositions();
    }
    
    public void addWidget( IWidget widget, boolean transition ){
        super.addWidget(widget, transition);
        
        _height += _widgetPaddingH;        
        _height += widget.getHeight();      
        
        widget = null;
        
        if( isExpanded() ){
            _currentHeight = _height;
            notifySubscribers( getEvet( Event.EVT_GUI_RESIZE ) );
        }                
       
    }
    
    public void insertWidget( int index, IWidget widget, boolean transition ){
        super.insertWidget(index, widget, transition);
            
        _height += _widgetPaddingH;        
        _height += widget.getHeight();       
        
        widget = null;
        
        if( isExpanded() ){
            _currentHeight = _height;
            notifySubscribers( getEvet( Event.EVT_GUI_RESIZE ) );
        }               
    }
    
    public void removeWidget( IWidget widget, boolean transition ){                      
        super.removeWidget( widget, transition );                        
      
        _height -= _widgetPaddingH;       
        _height -= widget.getHeight();
        
        widget = null;
        
        if( isExpanded() ){
            _currentHeight = _height;
            notifySubscribers( getEvet( Event.EVT_GUI_RESIZE ) );
        }                
        
    }        
    
    public int getHeight() {        
        if( _selectedWidget != -1 && _widgetCount > _selectedWidget ){
            return _widgets[_selectedWidget].getHeight();
        }
        else{
            return _currentHeight-_widgetPaddingH;
        }
    }
    
    public int getTotalHeight(){        
        if( _height == _currentHeight )
            return _currentHeight-(_widgetPaddingH<<1);
        else
            return _currentHeight-_widgetPaddingH;
    }
    
    protected void reinitSizeAndWidgetPositions(){
        if( _baseWidget == null ){
            super.reinitSizeAndWidgetPositions();
        }
        else{
            // align base widget
            if( _layout == Constants.LAYOUT_FLOW ){
                _baseWidget.setPos( _paddingX, _paddingY );                
            }
            else{
                // TODO: provide a more versitile way of positioning the widgets 
            }

            // check out the layer 
            if( _baseWidget.maxLayers() > _maxLayers )
                _maxLayers = _baseWidget.maxLayers(); 
            
            // align rest of the widgets offsetting from the size of the base widget
            super.reinitSizeAndWidgetPositions( _paddingX, _paddingY + _baseHeight );
            
        }
    }
    
    public boolean isAnimated(){
        return true; 
    }
    
    protected boolean outOfBounds( int wIndex, int movement ){
        // if last widget and int view then return false         
        IWidget widget = _widgets[wIndex];         
        
        if( movement == MOVEMENT_DOWN ){
//            System.out.println("getWidgetPosY " + getWidgetPosY(wIndex) + ", widget height " + widget.getTotalHeight() 
//                    + ", parent container height " + _parentContainer.getHeight()
//                    + ", parent offset " + _parentContainer.getOffsetY() 
//                    + ", parent y " + _parentContainer.getY() 
//                    + ", value: " +((getWidgetPosY(wIndex) ) > (_parentContainer.getY() + _parentContainer.getHeight() - _parentContainer.getOffsetY())));
            return ( getWidgetPosY(wIndex) + widget.getHeight() ) > ( _parentContainer.getHeight() - _parentContainer.getOffsetY() );
        }
        else if( movement == MOVEMENT_UP )
            return getWidgetPosY(wIndex) < _y;
        
        return false; 
    }
    
    public void handleEvent( Event evt ){
        if( evt._publisher == _baseWidget ){
            if( evt._type == Event.EVT_SELECT_DOWN ){
                setExpanded(!isExpanded());
            }
        }
        else{
            super.handleEvent( evt );
        }
    }       
    
    protected void adjustOffsetY( int prevWIndex, int wIndex ){        
        super.adjustOffsetY(prevWIndex, wIndex, false );
        _finalOffsetY = 0;
    }
}
